#include "dlm.h"
#include "stdio.h"
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/fcntl.h>
#include "../common/messages.h"
#include "../common/defines.h"


int DLM_open(dlm_t* dlm, char const* fifoFullPath){
    if(mkfifo(fifoFullPath, S_IREAD | S_IWRITE) != 0 && errno != EEXIST){
        fprintf(stderr, "Błąd tworzenia kolejki FIFO: %s\n", strerror(errno));
        return DLM_OPEN_ERR;
    }

    if((dlm->processFifo = open(fifoFullPath, O_RDWR)) == -1 && errno != EEXIST){
        fprintf(stderr, "Błąd otwarcia kolejki FIFO: %s\n", strerror(errno));
        return DLM_OPEN_ERR;
    }

    if((dlm->serverFifo = open(DLM_PIPE_NAME, O_WRONLY)) == -1 && errno != EEXIST){
        fprintf(stderr, "Błąd otwarcia kolejki FIFO serwera: %s\n", strerror(errno));
        return DLM_OPEN_ERR;
    }

    RequestMsg req;
    req.type = REQ_OPEN;
    req.fifoName = fifoFullPath;

    if(writeRequest(dlm->serverFifo, &req)){
        fprintf(stderr, "Błąd pisania do kolejki FIFO: %s\n", strerror(errno));
        return DLM_OPEN_ERR;
    }
    ResponseMsg res;
    if(readResponse(dlm->processFifo, &res)){
        fprintf(stderr, "Błąd czytania z kolejki FIFO: %s\n", strerror(errno));
        return DLM_OPEN_ERR;
    }
    if(res.status != STATUS_OK){
        fprintf(stderr, "Serwer odpowiedział błedem\n");
        return DLM_OPEN_ERR;
    }
    dlm->processId = res.processId;
    return DLM_OPEN_OK;
}

int DLM_add_resource(dlm_t *dlm, const int resourceId){
    RequestMsg req;
    req.type = REQ_ADD_RES;
    req.processId = dlm->processId;
    req.resourceId = resourceId;

    if(writeRequest(dlm->serverFifo, &req)){
        fprintf(stderr, "Błąd pisania do kolejki FIFO: %s\n", strerror(errno));
        return DLM_ADD_RES_ERR;
    }
    ResponseMsg res;
    if(readResponse(dlm->processFifo, &res)){
        fprintf(stderr, "Błąd czytania z kolejki FIFO: %s\n", strerror(errno));
        return DLM_ADD_RES_ERR;
    }
    if(res.status == STATUS_EXISTS){
        return DLM_ADD_RES_EXISTS;
    }else if(res.status == STATUS_OK){
        return DLM_ADD_RES_OK;
    }else{
        fprintf(stderr, "Serwer odpowiedział błedem\n");
        return DLM_ADD_RES_ERR;
    }
}

int DLM_del_resource(dlm_t *dlm, const int resourceId){
    RequestMsg req;
    req.type = REQ_DEL_RES;
    req.resourceId = resourceId;
    req.processId = dlm->processId;

    if(writeRequest(dlm->serverFifo, &req)){
        fprintf(stderr, "Błąd pisania do kolejki FIFO: %s\n", strerror(errno));
        return DLM_ADD_RES_ERR;
    }
    ResponseMsg res;
    if(readResponse(dlm->processFifo, &res)){
        fprintf(stderr, "Błąd czytania z kolejki FIFO: %s\n", strerror(errno));
        return DLM_DEL_RES_ERR;
    }

    if(res.status == STATUS_NOT_EXISTS){
        return DLM_DEL_RES_NOT_EXISTS;
    }if(res.status == STATUS_OK){
        return DLM_DEL_RES_OK;
    }else{
        fprintf(stderr, "Serwer odpowiedział błedem\n");
        return DLM_ADD_RES_ERR;
    }
}

int DLM_lock(dlm_t* dlm, const int resourceId, const int lockType, const long timeout){
    RequestMsg req;
    req.type = REQ_LOCK;
    req.processId = dlm->processId;
    req.resourceId = resourceId;
    req.lockType = lockType;
    req.timeout = timeout;

    if(writeRequest(dlm->serverFifo, &req)){
        fprintf(stderr, "Błąd pisania do kolejki FIFO: %s\n", strerror(errno));
        return DLM_LOCK_ERR;
    }
    ResponseMsg res;
    if(readResponse(dlm->processFifo, &res)){
        fprintf(stderr, "Błąd czytania z kolejki FIFO: %s\n", strerror(errno));
        return DLM_LOCK_ERR;
    }
    if(res.status == STATUS_TIMEOUT){
        return DLM_LOCK_TIMEOUT;
    }else if(res.status == STATUS_OK){
        return DLM_LOCK_OK;
    }else if(res.status == STATUS_EXISTS){
        return DLM_LOCK_EXISTS;
    }else{
        fprintf(stderr, "Serwer odpowiedział błedem\n");
        return DLM_LOCK_OK;
    }
}

int DLM_unlock(dlm_t *dlm, const int resourceId){
    RequestMsg req;
    req.type = REQ_UNLOCK;
    req.resourceId = resourceId;
    req.processId = dlm->processId;

    if(writeRequest(dlm->serverFifo, &req)){
        fprintf(stderr, "Błąd pisania do kolejki FIFO: %s\n", strerror(errno));
        return DLM_UNLOCK_ERR;
    }
    ResponseMsg res;
    if(readResponse(dlm->processFifo, &res)){
        fprintf(stderr, "Błąd czytania z kolejki FIFO: %s\n", strerror(errno));
        return DLM_UNLOCK_ERR;
    }

    if(res.status == STATUS_NOT_EXISTS){
        return DLM_UNLOCK_NOT_EXISTS;
    }else if(res.status == STATUS_OK){
        return DLM_UNLOCK_OK;
    }else{
        fprintf(stderr, "Serwer odpowiedział błedem\n");
        return DLM_UNLOCK_ERR;
    }
}

int DLM_trylock(dlm_t *dlm, const int resourceId, const int lockType){
    RequestMsg req;
    req.type = REQ_TRYLOCK;
    req.resourceId = resourceId;
    req.lockType = lockType;
    req.processId = dlm->processId;

    if(writeRequest(dlm->serverFifo, &req)){
        fprintf(stderr, "Błąd pisania do kolejki FIFO: %s\n", strerror(errno));
        return DLM_TRYLOCK_ERR;
    }
    ResponseMsg res;
    if(readResponse(dlm->processFifo, &res)){
        fprintf(stderr, "Błąd czytania z kolejki FIFO: %s\n", strerror(errno));
        return DLM_TRYLOCK_ERR;
    }

    if(res.status == STATUS_NOT_EXISTS){
        return DLM_TRYLOCK_NOT_EXISTS;
    }else if(res.status == STATUS_OK){
        //nie ma blokady
        return DLM_TRYLOCK_OK;
    }else if(res.status == STATUS_EXISTS){
        //jest blokada
        return DLM_TRYLOCK_FAILED;
    }else{
        fprintf(stderr, "Serwer odpowiedział błedem\n");
        return DLM_TRYLOCK_ERR;
    }
}

int DLM_close(dlm_t *dlm){
    RequestMsg req;
    req.type = REQ_CLOSE;
    req.processId = dlm->processId;

    if(writeRequest(dlm->serverFifo, &req)){
        fprintf(stderr, "Błąd pisania do kolejki FIFO: %s\n", strerror(errno));
        return DLM_CLOSE_ERR;
    }
    ResponseMsg res;
    if(readResponse(dlm->processFifo, &res)){
        fprintf(stderr, "Błąd czytania z kolejki FIFO: %s\n", strerror(errno));
        return DLM_CLOSE_ERR;
    }

    if(res.status == STATUS_OK){
        //TODO: spr return close()
        close(dlm->processFifo);
        close(dlm->serverFifo);
        return DLM_CLOSE_OK;
    }else{
        fprintf(stderr, "Serwer odpowiedział błedem\n");
        return DLM_CLOSE_ERR;
    }
}

